namespace Profit.Services
{
    using System;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Windows;
    using Core.Contracts.Interfaces;
    using Microsoft.Win32;

    
    /// <summary>
    /// Registry user settings
    /// </summary>
    internal sealed class RegistryUserSettings : IIsDisposed, INotifyPropertyChanged
    {
        private static RegistryUserSettings _default;

        /// <summary>
        /// Obtains the default single instance
        /// </summary>
        public static RegistryUserSettings Default
        {
            get
            {
                if(_default ==null)
                    _default = new RegistryUserSettings();

                return _default;
            }
        }

        private bool _disposed;
        private RegistryKey _registryKey;

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        internal RegistryUserSettings()
        {
            _registryKey = Registry.CurrentUser.OpenSubKey("Software\\Profit", RegistryKeyPermissionCheck.ReadWriteSubTree);
            if (_registryKey == null) 
                _registryKey = Registry.CurrentUser.CreateSubKey("Software\\Profit", RegistryKeyPermissionCheck.ReadWriteSubTree);
        
            if(_registryKey ==null)
                throw new ArgumentNullException("RegistryKey");
        }

        public double MainViewWidth
        {
            get { return GetValue<double>("MainViewWidth", 600); }
            set { SetValue("MainViewWidth", value, true, RegistryValueKind.String); }
        }
        public double MainViewHeight
        {
            get { return GetValue<double>("MainViewHeight", 400); }
            set { SetValue("MainViewHeight", value, true, RegistryValueKind.String); }
        }
        public GridLength RobotFeaturesColumnWidth
        {
            get { return GetValue("RobotFeaturesColumnWidth", new GridLength(1, GridUnitType.Star)); }
            set { SetValue("RobotFeaturesColumnWidth", value, true, RegistryValueKind.String); }
        }
        public GridLength FittingDetailsColumnWidth
        {
            get { return GetValue("FittingDetailsColumnWidth", new GridLength(1, GridUnitType.Star)); }
            set { SetValue("FittingDetailsColumnWidth", value, true, RegistryValueKind.String); }
        }
        public GridLength ItemsListColumnWidth
        {
            get { return GetValue("ItemsListColumnWidth", new GridLength(1, GridUnitType.Star)); }
            set { SetValue("ItemsListColumnWidth", value, true, RegistryValueKind.String); }
        }
        public GridLength ItemsListLowerRowHeight
        {
            get { return GetValue("ItemsListLowerRowHeight", new GridLength(1, GridUnitType.Star)); }
            set { SetValue("ItemsListLowerRowHeight", value, true, RegistryValueKind.String); }
        }
        public WindowState MainViewWindowState
        {
            get { return GetValue("MainViewWindowState", WindowState.Normal); }
            set { SetValue("MainViewWindowState", value, true, RegistryValueKind.String); }
        }
        public double ItemsListModulesViewNameColumnWidth
        {
            get { return GetValue<double>("ItemsListModulesViewNameColumnWidth", 100); }
            set { SetValue("ItemsListModulesViewNameColumnWidth", value, true, RegistryValueKind.String); }
        }
        public double ItemsListRobotsViewNameColumnWidth
        {
            get { return GetValue<double>("ItemsListRobotsViewNameColumnWidth", 100); }
            set { SetValue("ItemsListRobotsViewNameColumnWidth", value, true, RegistryValueKind.String); }
        }
        public GridLength FittingControlColumnWidth
        {
            get { return GetValue("FittingControlColumnWidth", new GridLength(3, GridUnitType.Star)); }
            set { SetValue("FittingControlColumnWidth", value, true, RegistryValueKind.String); }
        }
        public GridLength ItemsListUpperRowHeight
        {
            get { return GetValue("ItemsListUpperRowHeight", new GridLength(3, GridUnitType.Star)); }
            set { SetValue("ItemsListUpperRowHeight", value, true, RegistryValueKind.String); }
        }
        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <filterpriority>2</filterpriority>
        public void Dispose()
        {
            GC.SuppressFinalize(this);

            _disposed = true;

            if(_registryKey!=null)
            {
                _registryKey.Close();
                _registryKey.Dispose();
            }
            _registryKey = null;
        }

        /// <summary>
        /// Determines if the instance has been disposed
        /// </summary>
        public bool IsDisposed
        {
            get { return _disposed; }
        }

        private T GetValue<T>(string key, T defaultValue = default(T))
        {
            this.ThrowIfDisposed();
            if(_registryKey ==null) return defaultValue;

            var value = _registryKey.GetValue(key, defaultValue);
            try
            {
                if (typeof(T) == typeof(double))
                {
                    value = double.Parse(value.ToString());
                }
                else if (typeof(T) == typeof(GridLength))
                {
                    var conv = new GridLengthConverter();
                    value = conv.ConvertFromString(value.ToString());
                }
                else if (typeof(T) == typeof(WindowState))
                {
                    value = Enum.Parse(typeof(WindowState), value.ToString());
                }

                return (T)value;
            }
            catch (Exception exp)
            {
                Trace.TraceError("An exception of type \"{0}\" occured while reading registry key \"{1}\". Details : {2}",exp.GetType(),key,exp.Message);
                return defaultValue;
            }
        }

        private void SetValue<T>(string key, T value, bool notify, RegistryValueKind valueKind = RegistryValueKind.Unknown)
        {
            this.ThrowIfDisposed();
            if(_registryKey ==null) return;

            try
            {
                _registryKey.SetValue(key, value, valueKind);
            }
            catch (Exception exp)
            {
                Trace.TraceError("An exception of type \"{0}\" occured while writing registry key \"{1}\". Details : {2}", exp.GetType(), key, exp.Message);
                return;
            }

            if (notify)
                InvokePropertyChanged(key);
        }

        private void InvokePropertyChanged(string propertyName)
        {
            if(propertyName ==null) return;

            var handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}